package utils

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// NewAuthStateManager creates a new AuthStateManager instance
func NewAuthStateManager() *AuthStateManager {
	return &AuthStateManager{
		states: make(map[string]*AuthState),
		mutex:  sync.RWMutex{},
	}
}

// NewAuthStateManagerWithCleanup creates a new AuthStateManager with automatic cleanup
func NewAuthStateManagerWithCleanup(ctx context.Context, cleanupInterval time.Duration) *AuthStateManager {
	asm := &AuthStateManager{
		states: make(map[string]*AuthState),
		mutex:  sync.RWMutex{},
	}
	
	// Start background cleanup goroutine
	go asm.startBackgroundCleanup(ctx, cleanupInterval)
	
	return asm
}

// startBackgroundCleanup runs a background goroutine that periodically cleans up expired states
func (asm *AuthStateManager) startBackgroundCleanup(ctx context.Context, interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()
	
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			asm.CleanupExpiredStates()
		}
	}
}

// SetAuthState stores an authentication state for the given auth type
func (asm *AuthStateManager) SetAuthState(authType string, state *AuthState) error {
	if authType == "" {
		return NewAuthError(ErrTypeConfigInvalid, "auth type cannot be empty", "")
	}
	
	if state == nil {
		return NewAuthError(ErrTypeConfigInvalid, "auth state cannot be nil", "")
	}
	
	if err := state.Validate(); err != nil {
		return err
	}
	
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	// Set creation time if not already set
	if state.CreatedAt.IsZero() {
		state.CreatedAt = time.Now()
	}
	
	asm.states[authType] = state
	return nil
}

// GetAuthState retrieves an authentication state for the given auth type
func (asm *AuthStateManager) GetAuthState(authType string) (*AuthState, bool) {
	if authType == "" {
		return nil, false
	}
	
	asm.mutex.RLock()
	defer asm.mutex.RUnlock()
	
	state, exists := asm.states[authType]
	if !exists {
		return nil, false
	}
	
	// Check if the state has expired
	if state.IsExpired() {
		// Remove expired state (upgrade to write lock)
		asm.mutex.RUnlock()
		asm.mutex.Lock()
		delete(asm.states, authType)
		asm.mutex.Unlock()
		asm.mutex.RLock()
		return nil, false
	}
	
	return state, true
}

// ClearAuthState removes an authentication state for the given auth type
func (asm *AuthStateManager) ClearAuthState(authType string) {
	if authType == "" {
		return
	}
	
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	delete(asm.states, authType)
}

// IsExpired checks if the authentication state for the given auth type has expired
func (asm *AuthStateManager) IsExpired(authType string) bool {
	if authType == "" {
		return true
	}
	
	asm.mutex.RLock()
	defer asm.mutex.RUnlock()
	
	state, exists := asm.states[authType]
	if !exists {
		return true
	}
	
	return state.IsExpired()
}

// GetActiveTokens returns a map of active (non-expired) tokens
func (asm *AuthStateManager) GetActiveTokens() map[string]string {
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	activeTokens := make(map[string]string)
	expiredTypes := make([]string, 0)
	
	for authType, state := range asm.states {
		if state.IsExpired() {
			expiredTypes = append(expiredTypes, authType)
		} else {
			activeTokens[authType] = state.Token
		}
	}
	
	// Clean up expired states
	for _, authType := range expiredTypes {
		delete(asm.states, authType)
	}
	
	return activeTokens
}

// GetAllAuthStates returns a copy of all authentication states
func (asm *AuthStateManager) GetAllAuthStates() map[string]*AuthState {
	asm.mutex.RLock()
	defer asm.mutex.RUnlock()
	
	states := make(map[string]*AuthState)
	for authType, state := range asm.states {
		// Create a copy to avoid external modifications
		stateCopy := &AuthState{
			Token:     state.Token,
			TokenType: state.TokenType,
			ExpiresAt: state.ExpiresAt,
			Metadata:  make(map[string]string),
			CreatedAt: state.CreatedAt,
		}
		
		// Copy metadata
		for k, v := range state.Metadata {
			stateCopy.Metadata[k] = v
		}
		
		states[authType] = stateCopy
	}
	
	return states
}

// HasAuthType checks if an authentication state exists for the given auth type
func (asm *AuthStateManager) HasAuthType(authType string) bool {
	_, exists := asm.GetAuthState(authType)
	return exists
}

// CleanupExpiredStates removes all expired authentication states
func (asm *AuthStateManager) CleanupExpiredStates() int {
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	expiredTypes := make([]string, 0)
	
	for authType, state := range asm.states {
		if state.IsExpired() {
			expiredTypes = append(expiredTypes, authType)
		}
	}
	
	// Remove expired states
	for _, authType := range expiredTypes {
		delete(asm.states, authType)
	}
	
	return len(expiredTypes)
}

// GetAuthStateCount returns the number of stored authentication states
func (asm *AuthStateManager) GetAuthStateCount() int {
	asm.mutex.RLock()
	defer asm.mutex.RUnlock()
	
	return len(asm.states)
}

// Clear removes all authentication states
func (asm *AuthStateManager) Clear() {
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	asm.states = make(map[string]*AuthState)
}

// UpdateAuthState updates specific fields of an existing authentication state
func (asm *AuthStateManager) UpdateAuthState(authType string, updater func(*AuthState) error) error {
	if authType == "" {
		return NewAuthError(ErrTypeConfigInvalid, "auth type cannot be empty", "")
	}
	
	if updater == nil {
		return NewAuthError(ErrTypeConfigInvalid, "updater function cannot be nil", "")
	}
	
	asm.mutex.Lock()
	defer asm.mutex.Unlock()
	
	state, exists := asm.states[authType]
	if !exists {
		return NewAuthError(ErrTypeAuthRequired, 
			"authentication state not found", 
			fmt.Sprintf("auth type: %s", authType))
	}
	
	// Check if expired before updating
	if state.IsExpired() {
		delete(asm.states, authType)
		return NewAuthError(ErrTypeTokenExpired,
			"authentication state has expired",
			fmt.Sprintf("auth type: %s", authType))
	}
	
	// Apply the update
	if err := updater(state); err != nil {
		return err
	}
	
	// Validate the updated state
	if err := state.Validate(); err != nil {
		return err
	}
	
	return nil
}